﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace Common.IO.FileMapping
{
    /// <summary>
    /// Maps file content to object(s)
    /// Ex 1: Xml file nodes to class properties
    /// Ex 2: CSV file columns to class properties
    /// </summary>
    public static class FileMapper
    {
        public static List<T> GetObjects<T>()
        {
            if (MapConfig.xdoc == null) throw new Exception("Must load Config file first;");
            //Process CSV File
            {
                XDocument xdoc = MapConfig.xdoc;
                XElement srcName = xdoc.XPathSelectElement("/FileMapper[@version='1']/Source[@type='csv']");

                string CSVFileName = srcName.Attribute("loc").Value;

                if (!File.Exists(CSVFileName)) throw new FileNotFoundException("CSV was not found!", CSVFileName);

                string[] CSVlines = File.ReadAllLines(CSVFileName);
                string[] CSVColums = null;

                ConcurrentQueue<T> objlst = new ConcurrentQueue<T>();
                Type tType = typeof(T);

                XElement mapName = xdoc.XPathSelectElement(String.Format("/FileMapper[@version='1']/Source[@type='csv']/Map[@destClass='{0}, {1}']", tType.FullName, tType.Namespace));
                // Create Types and Objects
                string destClass = mapName.Attribute("destClass").Value;
                object destObj = TypeEx.Create(destClass);
                IDictionary<string, string> Mapping = new Dictionary<string, string>();

                mapName.Elements("Property").ForEach(i => Mapping.Add(i.Attribute("src").Value, i.Attribute("dest").Value));

                Parallel.ForEach(CSVlines, x =>
                    {
                        CSVColums = x.Split(new char[] { ',' });

                        destObj = CSVColums.BindPropertiesValues(destClass, Mapping);
                        objlst.Enqueue(destObj.ToType<T>());
                    }
                );

                return objlst.ToList<T>();
            }
        }
    }
}
